home *** CD-ROM | disk | FTP | other *** search
/ Programmer Power Tools / Programmer Power Tools.iso / progjrn / pj_7_6.arc / ALTMON.PAS next >
Pascal/Delphi Source File  |  1989-06-07  |  16KB  |  570 lines

  1. Unit AltMon;
  2. {
  3.   Overview/Description:
  4.     AltMon provides support which allows an application to utilize two video
  5.     monitors.  Even though it is possible, in some cases, to keep two video
  6.     adapters on the bus, without conflict, MS-DOS and Turbo-Pascal are only
  7.     able to utilize one at a time.
  8.  
  9.     AltMon gets around this limitation by writing directly to the video RAM
  10.     on the idle monitor.  Normal system services on the default monitor are
  11.     not inhibited in any fashion.  The net result is two active video
  12.     displays.
  13.  
  14.     Most of the routines within AltMon are prefixed with an "Alt_", for
  15.     ease in identification, and to avoid name conflicts.  All of the
  16.     intrinsic Turbo Pascal video support routines have been implemented.
  17.  
  18.     For example, to clear the alternate display screen, simply invoke
  19.     "Alt_ClrScr" or to position the alternate cursor, use "Alt_GotoXY(X, Y)".
  20.  
  21.     Most routines are self explanatory, but AltMon_Setup must be called
  22.     prior to invoking any of the routines in this module.
  23.  
  24.   Compiler/Operating System:
  25.     Turbo-Pascal Ver 5.0
  26.     MS-DOS Ver 3.30
  27.  
  28.   Maintenance History:
  29.     7 June, 1989    G.S.Cole    //Creation//
  30. }
  31. Interface
  32.  
  33. Uses DOS;
  34.  
  35. Type
  36.   Alt_String = String[255];
  37.  
  38.   AltMon_Attribute_Type = (Alt_Normal, Alt_Intense, Alt_Underlined, Alt_Intense_Underlined,
  39.                            Alt_Reverse, Alt_Blink, Alt_Blink_Intense, Alt_Blink_Reverse);
  40. {.Page}
  41. {
  42.     Video Attribute Group.
  43. }
  44. Function Alt_TextAttr:Byte;
  45. Function Alt_Get_TextAttr:AltMon_Attribute_Type;
  46. Procedure Alt_Set_Attribute(Candidate:AltMon_Attribute_Type);
  47. Procedure Alt_NormVideo;
  48. Procedure Alt_HighVideo;
  49. Procedure Alt_LowVideo;
  50. Procedure Alt_TextBackGround(Color:Byte);
  51. Procedure Alt_TextColor(Color:Byte);
  52. {
  53.     Cursor Support Group.
  54. }
  55. Procedure Alt_Cursor_Enable;
  56. Procedure Alt_Cursor_Disable;
  57. Procedure Alt_GotoXY(X, Y:Byte);
  58. Function Alt_WhereX:Byte;
  59. Function Alt_WhereY:Byte;
  60. {
  61.     Window Support Group.
  62. }
  63. Function Alt_WindMin:Word;
  64. Function Alt_WindMax:Word;
  65. Procedure Alt_Window(X1, Y1, X2, Y2:Byte);
  66. {
  67.     Display support.
  68. }
  69. Procedure Alt_ClrScr;
  70. Procedure Alt_ClrEOL;
  71. Procedure Alt_DelLine;
  72. Procedure Alt_InsLine;
  73. {
  74.     ...And something to write with...
  75. }
  76. Procedure Alt_Write(Buffer:Alt_String);
  77. Procedure Alt_Writeln(Buffer:Alt_String);
  78. {
  79.     Utilities
  80. }
  81. Function Swap_Monitor:Boolean;
  82. Procedure AltMon_Setup;
  83. {
  84.     Initialization...
  85. }
  86. Function Dual_Monitors_Detected:Boolean;
  87.  
  88. Implementation
  89. {.Page}
  90. CONST
  91.   Cursor_Attribute = $F0;
  92.   Cursor_Character = $DB;
  93.   Video_RAM = $B000;
  94.   Other_Video_RAM = $B800;
  95.  
  96.   X_Max = 79;
  97.   X_Min = 0;
  98.   Y_Max = 24;
  99.   Y_Min = 0;
  100.  
  101. Type
  102.   Display_Record = Record
  103.     C:Char; { the character to be displayed }
  104.     A:Byte; { and it's video attribute }
  105.   end;
  106.  
  107.   Line_Type = Array[X_Min..X_Max] of Display_Record;
  108.   Display_Type = Array[Y_Min..Y_Max] of Line_Type;
  109.  
  110. VAR
  111.   Display: Display_Type Absolute Video_RAM:0;
  112.   Attribute_Type:AltMon_Attribute_Type;
  113.   Attribute,            
  114.   Cur_X, Cur_Y,
  115.   Cur_X_Min, Cur_Y_Min, Cur_X_Max, Cur_Y_Max:Byte;
  116.   Cursor_Enable_Flag:Boolean;
  117.  
  118. Procedure Post_Cursor;
  119. {
  120.     Called after moving cursor, ensures (if enabled) a cursor is present.
  121. }
  122. begin { Procedure Post_Cursor }
  123.   If Cursor_Enable_Flag then
  124.     begin
  125.       If Display[Cur_Y, Cur_X].C = ' '
  126.         then Display[Cur_Y, Cur_X].C := Chr(Cursor_Character);
  127.       Display[Cur_Y, Cur_X].A := Cursor_Attribute;
  128.     end;
  129. end; { Procedure Post_Cursor }
  130.  
  131. Procedure Pre_Cursor;
  132. {
  133.     Invoked prior to moving cursor, ensures it is totally deleted.
  134. }
  135. begin { Procedure Pre_Cursor }
  136.   If Cursor_Enable_Flag then
  137.     begin
  138.       If Display[Cur_Y, Cur_X].C = Chr(Cursor_Character)
  139.         then Display[Cur_Y, Cur_X].C := ' ';
  140.       Display[Cur_Y, Cur_X].A := Attribute;
  141.     end;
  142. end; { Procedure Pre_Cursor }
  143. {.Page}
  144. Procedure Check_4_Scroll;
  145. {
  146.     Determine if vertical scrolling is required, and if so, perform it.
  147.     Scrolling is performed against the logical window boundaries, not
  148.     the physical limits of the display.
  149. }
  150. var
  151.   X, Y:Byte;
  152. begin { Procedure Check_4_Scroll }
  153.   if Cur_Y > Cur_Y_Max then
  154.     begin
  155.       For Y := Cur_Y_Min to Pred(Cur_Y_Max) do        { bump lines up one }
  156.         Move(Display[Y+1, Cur_X_Min], Display[Y, Cur_X_Min], (Succ(Cur_X_Max-Cur_X_Min)*2));
  157.       For X := Cur_X_Min to Cur_X_Max do              { clear off bottom line }
  158.         begin
  159.           Display[Cur_Y_Max, X].C := ' ';
  160.           Display[Cur_Y_Max, X].A := Attribute;
  161.         end;
  162.       Cur_Y := Cur_Y_Max;
  163.     end;
  164.   Post_Cursor;
  165. end; { Procedure Check_4_Scroll }
  166. {.Page}
  167. Procedure Check_4_CRLF;
  168. {
  169.     Determine if the end of line has been exceeded.
  170.     If so, wrap to the next line, scrolling if required.
  171. }
  172. begin { Procedure Check_4_CRLF }
  173.   If Cur_X > Cur_X_Max then
  174.     begin
  175.       Cur_X := Cur_X_Min;
  176.       Cur_Y := Succ(Cur_Y);
  177.       Check_4_Scroll;
  178.     end;
  179.   Post_Cursor;
  180. end; { Procedure Check_4_CRLF }
  181.  
  182. Procedure Service_Control_Character(C:Char);
  183. {
  184.     Perform special handling to support the legal control characters.
  185. }
  186. begin { Procedure Service_Control_Character }
  187.   Case C of
  188.     ^G:Write(^G);  { Bell }
  189.     ^H:begin       { Backspace }
  190.          If Cur_X > Cur_X_Min then
  191.            begin
  192.              Pre_Cursor;
  193.              Cur_X := Pred(Cur_X);
  194.              Post_Cursor;
  195.            end;
  196.        end;
  197.     ^J:begin       { Line Feed }
  198.         Pre_Cursor;
  199.         Cur_Y := Succ(Cur_Y);
  200.         Check_4_Scroll;
  201.        end;
  202.     ^M:begin       { Carriage Return }
  203.          Pre_Cursor;
  204.          Cur_X := Cur_X_Min;
  205.          Post_Cursor;
  206.        end;
  207.   end; { case }
  208. end; { Procedure Service_Control_Character }
  209. {.Page}
  210. Function Alt_TextAttr:Byte;
  211. {
  212.     Return the current alternate monitor attribute.
  213.     Note that this differs from the Turbo Pascal implementation in that
  214.     this is a function, and cannot be written to (TextAttr is a variable).
  215.     You can manually set video attributes via Alt_TextBackGround and
  216.     Alt_TextColor.
  217. }
  218. begin { Function Alt_TextAttr }
  219.   Alt_TextAttr := Attribute;
  220. end; { Function Alt_TextAttr }
  221.  
  222. Function Alt_Get_TextAttr:AltMon_Attribute_Type;
  223. {
  224.     Return the current alternate monitor attribute types.
  225.  
  226.     There is not an equivalent intrinsic Turbo Pascal procedure.
  227. }
  228. begin { Function Alt_Get_TextAttr }
  229.   Alt_Get_TextAttr := Attribute_Type;
  230. end; { Function Alt_Get_TextAttr }
  231.  
  232. Procedure Alt_Set_Attribute(Candidate:AltMon_Attribute_Type);
  233. {
  234.     Define the current video attribute.  Note that these are set with
  235.     typed variables, rather than specifying the bit definitions.
  236.  
  237.     There is not an equivalent intrinsic Turbo Pascal procedure.
  238. }
  239. begin { Procedure Alt_Set_Typed_Attribute }
  240.   Attribute_Type := Candidate;
  241.   Case Candidate of
  242.     Alt_Normal:     Attribute := 7;
  243.     Alt_Intense:    Attribute := 15;
  244.     Alt_Underlined: Attribute := 1;
  245.     Alt_Intense_Underlined: Attribute := 9;
  246.     Alt_Reverse:    Attribute := 112;
  247.     Alt_Blink:      Attribute := 135;
  248.     Alt_Blink_Intense: Attribute := 143;
  249.     Alt_Blink_Reverse: Attribute := 240;
  250.   end;
  251. end; { Procedure Alt_Set_Attribute }
  252. {.Page}
  253. Procedure Alt_NormVideo;
  254. {
  255.     Set the normal video attributes.
  256. }
  257. begin { Procedure Alt_NormVideo }
  258.   Alt_Set_Attribute(Alt_Normal);
  259. end; { Procedure Alt_NormVideo }
  260.  
  261. Procedure Alt_HighVideo;
  262. {
  263.     Set highlighted (intense) attributes.
  264. }
  265. begin { Procedure Alt_HighVideo }
  266.   Alt_Set_Attribute(Alt_Intense);
  267. end; { Procedure Alt_HighVideo }
  268.  
  269. Procedure Alt_LowVideo;
  270. {
  271.     My tests show that this is the same video attributes as NormVideo.
  272. }
  273. begin { Procedure Alt_LowVideo }
  274.   Alt_NormVideo;
  275. end; { Procedure Alt_LowVideo }
  276.  
  277. Procedure Alt_TextBackGround(Color:Byte);
  278. {
  279.     Insert a new background color into the attribute mask.
  280. }
  281. begin { Procedure Alt_TextBackGround }
  282.   Color := (Color and 7)*16;       { mask background, position for insertion }
  283.   Attribute := Attribute and $8F;  { mask background }
  284.   Attribute := Attribute or Color;
  285. end; { Procedure Alt_TextBackGround }
  286.  
  287. Procedure Alt_TextColor(Color:Byte);
  288. {
  289.     Insert a new text color into the attribute mask.
  290.     Blinking enabled, if leading bit set.
  291. }
  292. begin { Procedure Alt_TextColor }
  293.   Color := Color and $1F;          { mask background, leave blink bit }
  294.   Attribute := Attribute and $70;  { mask foreground, and blink bit }
  295.   Attribute := Attribute or Color; { new result }
  296. end; { Procedure Alt_TextColor }
  297. {.Page}
  298. Procedure Alt_ClrScr;
  299. {
  300.     "Clear" the alternate display by setting the video attributes to normal,
  301.     and padding it out with ASCII spaces.
  302.     Exits w/cursor "homed" - respects the logical window definitions.
  303. }
  304. VAR
  305.   X, Y:Byte;
  306. begin { Procedure Alt_ClrScr }
  307.   For X := Cur_X_Min to Cur_X_Max do              { clear off top line }
  308.     begin
  309.       Display[Cur_Y_Min, X].C := ' ';
  310.       Display[Cur_Y_Min, X].A := Attribute;
  311.     end;
  312.   For Y := Cur_Y_Min to Pred(Cur_Y_Max) do        { and copy on down... }
  313.     Move(Display[Y, Cur_X_Min], Display[Y+1, Cur_X_Min], (Succ(Cur_X_Max-Cur_X_Min)*2));
  314.   Cur_X := Cur_X_Min;
  315.   Cur_Y := Cur_Y_Min;
  316.   Post_Cursor;
  317. end; { Procedure Alt_ClrScr }
  318.  
  319. Procedure Alt_ClrEOL;
  320. {
  321.     Clear the current line, from the current cursor position to end,
  322.     without altering the current cursor location.
  323. }
  324. VAR
  325.   X:Byte;
  326. begin { Procedure Alt_ClrEOL }
  327.   For X := Cur_X to Cur_X_Max do
  328.     begin
  329.       Display[Cur_Y, X].C := ' ';
  330.       Display[Cur_Y, X].A := Attribute;
  331.     end;
  332.   Post_Cursor;
  333. end; { Procedure Alt_ClrEOL }
  334.  
  335. Procedure Alt_Cursor_Enable;
  336. {
  337.     Enable the pseudo cursor on the alternate display.
  338. }
  339. begin { Procedure Alt_Cursor_Enable }
  340.   Cursor_Enable_Flag := True;
  341.   Post_Cursor;
  342. end; { Procedure Alt_Cursor_Enable }
  343.  
  344. Procedure Alt_Cursor_Disable;
  345. {
  346.     Disable the psuedo cursor on the alternate display.
  347. }
  348. begin { Procedure Alt_Cursor_Disable }
  349.   Pre_Cursor;
  350.   Cursor_Enable_Flag := False;
  351. end; { Procedure Alt_Cursor_Disable }
  352. {.Page}
  353. Procedure Alt_GotoXY(X, Y:Byte);
  354. {
  355.     Position the pseudo-cursor.  Note that the coordinates are window
  356.     relative, and if invalid, the cursor isn't moved.
  357. }
  358. begin { Procedure Alt_GotoXY }
  359.   X := Pred(X);  { Ref to 0, 0 }
  360.   Y := Pred(Y);
  361.   If (X <= Cur_X_Max) or (Y <= Cur_Y_Max) then
  362.     begin
  363.       Pre_Cursor;
  364.       Cur_X := X + Cur_X_Min;
  365.       Cur_Y := Y + Cur_Y_Min;
  366.       Post_Cursor;
  367.     end;
  368. end; { Procedure Alt_GotoXY }
  369.  
  370. Function Alt_WhereX:Byte;
  371. {
  372.     Return the X coordinate of the cursor.
  373.     Reported position is window relative.
  374. }
  375. begin { Function Alt_WhereX }
  376.   Alt_WhereX := Succ(Cur_X - Cur_X_Min);
  377. end; { Function Alt_WhereX }
  378.  
  379. Function Alt_WhereY:Byte;
  380. {
  381.     Return the Y coordinate of the cursor.
  382.     Reported position is window relative.
  383. }
  384. begin { Function Alt_WhereY }
  385.   Alt_WhereY := Succ(Cur_Y - Cur_Y_Min);
  386. end; { Function Alt_WhereY }
  387.  
  388. Function Alt_WindMin:Word;
  389. {
  390.     Return the current upper left corner window coordinates, as packed value.
  391. }
  392. begin { Function Alt_WindMin }
  393.   Alt_WindMin := Cur_Y_Min * 256 + Cur_X_Min;
  394. end; { Function Alt_WindMin }
  395.  
  396. Function Alt_WindMax:Word;
  397. {
  398.     Return the current lower right corner window coordinates, as packed value.
  399. }
  400. begin { Function Alt_WindMax }
  401.   Alt_WindMax := Cur_Y_Max * 256 + Cur_X_Max;
  402. end; { Function Alt_WindMax }
  403. {.Page}
  404. Procedure Alt_DelLine;
  405. {
  406.     Delete the line at the current cursor location, and scroll up the lines
  407.     underneath it.
  408. }
  409. VAR
  410.   X, Y:Byte;
  411. begin { Procedure Alt_DelLine }
  412.   Pre_Cursor;
  413.   For Y := Cur_Y to Pred(Cur_Y_Max) do
  414.     Move(Display[Y+1, Cur_X_Min], Display[Y, Cur_X_Min], (Succ(Cur_X_Max-Cur_X_Min)*2));
  415.   For X := Cur_X_Min to Cur_X_Max do              { clear off bottom line }
  416.     begin
  417.       Display[Cur_Y_Max, X].C := ' ';
  418.       Display[Cur_Y_Max, X].A := Attribute;
  419.     end;
  420.   Post_Cursor;
  421. end; { Procedure Alt_DelLine }
  422.  
  423. Procedure Alt_InsLine;
  424. {
  425.     Insert a line at the current cursor location.  What this really means
  426.     is bump all the lines down, and clear the current line.
  427. }
  428. VAR
  429.   X, Y:Byte;
  430. begin { Procedure Alt_InsLine }
  431.   Pre_Cursor;
  432.   For Y := Pred(Cur_Y_Max) downto Cur_Y do
  433.     Move(Display[Y, Cur_X_Min], Display[Y+1, Cur_X_Min], (Succ(Cur_X_Max-Cur_X_Min)*2));
  434.  
  435.   For X := Cur_X_Min to Cur_X_Max do              { clear off current line }
  436.     begin
  437.       Display[Cur_Y, X].C := ' ';
  438.       Display[Cur_Y, X].A := Attribute;
  439.     end;
  440.  
  441.   Cur_X := Cur_X_Min;
  442.   Post_Cursor;
  443. end; { Procedure Alt_InsLine }
  444. {.Page}
  445. Procedure Alt_Window(X1, Y1, X2, Y2:Byte);
  446. {
  447.     Define a portion of the alternate display as the current window.
  448.     All display coordinates will be reference to the new logical coordinates.
  449.     Exits w/cursor "homed".
  450. }
  451. begin { Procedure Alt_Window }
  452.   Pre_Cursor;
  453.   Cur_X_Min := Pred(X1);
  454.   Cur_Y_Min := Pred(Y1);
  455.   Cur_X_Max := Pred(X2);
  456.   Cur_Y_Max := Pred(Y2);
  457.   Cur_X := Cur_X_Min;
  458.   Cur_Y := Cur_Y_Min;
  459.   Post_Cursor;
  460. end; { Procedure Alt_Window }
  461.  
  462. Procedure Alt_Write(Buffer:Alt_String);
  463. {
  464.     Write a line of data to the display.
  465. }
  466. VAR
  467.   I:Byte;
  468. begin { Procedure Alt_Write }
  469.   Pre_Cursor;
  470.   Check_4_Scroll;
  471.   For I := 1 to Length(Buffer) do
  472.     begin
  473.       If Buffer[I] < ' '
  474.         then Service_Control_Character(Buffer[I])
  475.         else begin
  476.           Display[Cur_Y, Cur_X].C := Buffer[I];
  477.           Display[Cur_Y, Cur_X].A := Attribute;
  478.           Cur_X := Succ(Cur_X);
  479.           Check_4_CRLF;
  480.         end;
  481.     end;
  482. end; { Procedure Alt_Write }
  483. {.Page}
  484. Procedure Alt_Writeln(Buffer:Alt_String);
  485. {
  486.     Write a line of data to the display, followed by a CR/LF sequence.
  487. }
  488. begin { Procedure Alt_Writeln }
  489.   Buffer := Buffer + ^M + ^J;
  490.   Alt_Write(Buffer);
  491. end; { Procedure Alt_Writeln }
  492.  
  493. Function Dual_Monitors_Detected:Boolean;
  494. {
  495.     Determine if two video adapters are present on the buss.  This is
  496.     accomplished by writing to the origin of both the monochrome and
  497.     color video display pages.  If I'm allowed to change the contents
  498.     of these memory locations, then two video adapters are probably
  499.     present, and TRUE is returned.
  500. }
  501. VAR
  502.   C:Byte;
  503. begin { Function Dual_Monitors_Detected }
  504.   C := MEM[Video_RAM:0];
  505.   MEM[Video_RAM:0] := Succ(C);
  506.   If C <> MEM[Video_RAM:0] then
  507.     begin
  508.       MEM[Video_RAM:0] := C;
  509.       C := MEM[Other_Video_RAM:0];
  510.       MEM[Other_Video_RAM:0] := Succ(C);
  511.       If C = MEM[Other_Video_RAM:0]
  512.         then Dual_Monitors_Detected := False
  513.         else begin
  514.           MEM[Other_Video_RAM:0] := C;
  515.           Dual_Monitors_Detected := True;
  516.         end;
  517.     end
  518.     else Dual_Monitors_Detected := False;
  519. end; {Function Dual_Monitors_Detected }
  520. {.Page}
  521. Function Swap_Monitor:Boolean;
  522. {
  523.     Swap the active display.
  524.     If current display is monochrome, make it color, and return true.
  525.     If current display is color, make it monochrome, and return false.
  526. }
  527. CONST
  528.   Video_Interrupt = $10;
  529.   Equipment_List_Offset = $410;
  530. VAR
  531.   Regs:Registers;
  532.   X:Byte;
  533. begin { Function Swap_Monitor }
  534.   Regs.AH := $0F;     { Determine Video Mode }
  535.   INTR(Video_Interrupt, Regs);
  536.   X := MEM[0:Equipment_List_Offset];
  537.   If Regs.AL = 7 then { Mono Mode? }
  538.     begin  { Set Color Mode }
  539.       X := X AND $CF;
  540.       Regs.AL := 3;
  541.       Swap_Monitor := True;
  542.     end
  543.   else
  544.     begin  { Set Mono Mode }
  545.       X := X OR $30;
  546.       Regs.AL := 7;
  547.       Swap_Monitor := False;
  548.     end;
  549.   MEM[0:Equipment_List_Offset] := X;
  550.   Regs.AH := 0;       { Set Video Mode }
  551.   INTR(Video_Interrupt, Regs);
  552. end; { Function Swap_Monitor }
  553.  
  554. Procedure AltMon_Setup;
  555. {
  556.   Prepare to use the AltMon module by initializing variables, and clearing
  557.     the alternate display.
  558. }
  559. begin { Procedure AltMon_Setup }
  560.   Alt_NormVideo;
  561.   Cur_X_Min := X_Min;
  562.   Cur_Y_Min := Y_Min;
  563.   Cur_X_Max := X_Max;
  564.   Cur_Y_Max := Y_Max;
  565.   Alt_ClrScr;
  566.   Alt_Cursor_Enable;
  567. end; { Procedure AltMon_Setup }
  568.  
  569. end. { Unit AltMon }
  570.